<script setup lang="ts">
import {
    AmbientLight,
    AnimationMixer,
    Clock,
    EquirectangularReflectionMapping,
    type Group,
    PerspectiveCamera,
    Scene,
    TextureLoader,
} from 'three';
import { onMounted, ref, useTemplateRef } from 'vue';
import { ThreeUtil } from 'exploria-ui/src/utils/ThreeUtil.ts';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

// 网格地面
const threeBaseRef = useTemplateRef<HTMLElement>('three-base');

threeBaseRef.value!.getBoundingClientRect();

let fps = ref(30);

const currentSliderValue = ref<number>(0); // 滑块位置   单位是帧
const sliderMaxValue = ref<number>(100); // 动画总帧数

const isLoading = ref(false);
const modelLoadingProgress = ref<number>(0);
const clock = new Clock();
onMounted(() => {
    const scene = new Scene();
    const canvas = threeBaseRef.value?.querySelector(
        'canvas',
    ) as HTMLCanvasElement;

    const camera = new PerspectiveCamera(
        70,
        canvas.offsetWidth / canvas.offsetHeight,
        1, // 最近
        10000, // 最远
    );
    camera.position.set(0, 200, 500);
    camera.lookAt(0, 20, 0);

    const renderer = ThreeUtil.createWebGLRenderer(canvas); // 创建渲染器
    renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);

    // 创建控制器
    const controls = new OrbitControls(camera, canvas);
    controls.enableDamping = true; // 使动画循环使用时阻尼或自转 意思是否有惯性
    controls.dampingFactor = 0.95; // 阻尼系数，越大则惯性越大
    controls.screenSpacePanning = false; // 右键拖拽时是否在屏幕空间中
    controls.minDistance = 300; // 相机距离原点的最小距离
    controls.maxDistance = 800; // 相机距离原点的最大距离
    controls.maxPolarAngle = Math.PI * 0.45; // 相机距离原点的最大角度
    controls.enablePan = false; // 关闭移动

    // 添加环境光
    const ambientLight = new AmbientLight(0xffffff, 4);
    scene.add(ambientLight);

    /**
     * 添加环境贴图
     */
    // 将渐变纹理设置为场景的环境贴图和背景
    const textureLoader = new TextureLoader();

    const envMapTexture = textureLoader.load(
        '/threejs/model/scene/EnvMapTexture.jpg',
    );
    envMapTexture.mapping = EquirectangularReflectionMapping; //正常只是一张图平铺，设置这个可以让图包围环绕整个环境
    scene.environment = envMapTexture;
    scene.background = envMapTexture;

    // 添加地板
    ThreeUtil.loadGlb('/threejs/model/scene/羽毛球场.glb').then((model) => {
        const floor = model.scenes[0]!;
        floor.castShadow = true;
        floor.scale.set(70, 70, 70);
        scene.add(floor);
    });

    // —————————————————————————————————— 加载模型 ————————————————————————————————————————
    isLoading.value = true;
    modelLoadingProgress.value = 0;
    ThreeUtil.loadFBX('/threejs/model/motion/单脚内侧踢球.fbx', (xhr) => {
        modelLoadingProgress.value = parseFloat(
            ((xhr.loaded / xhr.total) * 100).toFixed(2),
        );
    })
        .then((model: Group) => {
            modelLoadingProgress.value = 100;
            // data.scale.set(0.01, 0.01, 0.01);
            model.castShadow = true;

            // 将模型添加到场景中
            scene.add(model);

            // ———————————————————————————— 创建动画混合器 ———————————————————————————————————————
            const mixer = new AnimationMixer(model);

            // ———————————————————————————— 创建动作 ———————————————————————————————————————
            const action = mixer.clipAction(model.animations[0]!);
            action.play();

            (function animate() {
                mixer.update(clock.getDelta());
                currentSliderValue.value = action.time * fps.value;
                sliderMaxValue.value = action.getClip().duration * fps.value;
                // 只有加了这个才能保证同步，注意注意

                controls.update();
                renderer.render(scene, camera);
                requestAnimationFrame(animate);
            })();
        })
        .finally(() => {
            isLoading.value = false;
        });
});
</script>

<template>
    <div class="three-base" ref="three-base">
        <canvas />
    </div>
</template>

<style scoped lang="scss">
.three-base {
    canvas {
        width: 100%;
        height: 400px;
    }
}
</style>
